package com.wanlian;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

public class Download {
    private static int threadCount =2;// 开启2个线程
    private static int blockSize = 0;// 每个线程下载的大小
    private static int runningTrheadCount = 0;// 当前运行的线程数
    private static String path = "http://47.75.195.106/router/socketserver/file/dir/8a29db281b524b22881912db6a03e598.png";
    private static String clientPath="/Users/zhaoxiaotong/Downloads/";


    public static void main(String[] args) {
        try {
            // 1.请求url地址获取服务端资源的大小
            URL url = new URL(path);
            HttpURLConnection openConnection = (HttpURLConnection) url.openConnection();
            openConnection.setRequestMethod("GET");
            openConnection.setConnectTimeout(10 * 1000);
            int code = openConnection.getResponseCode();
            if (code == 200) {
                // 获取资源的大小
                //如果该资源的大小未知或是其字节比Integer.MAX_VALUE(非常非常接近2G)还大,则返回-1
                int filelength = openConnection.getContentLength();

                String fileName = getFileName(path);
                // 2.在本地创建一个与服务端资源同样大小的一个文件（占位）
                RandomAccessFile randomAccessFile = new RandomAccessFile(new File(clientPath+fileName), "rw");
                randomAccessFile.setLength(filelength);// 设置随机访问文件的大小,本地的占位文件
                randomAccessFile.close();
                // 3.要分配每个线程下载文件的开始位置和结束位置。开始分段
                blockSize = filelength / threadCount;// 计算出每个线程理论下载大小
                for (int threadId = 0; threadId < threadCount; threadId++) {
                    int startIndex = threadId * blockSize;// 计算每个线程下载的开始位置
                    int endIndex = (threadId + 1) * blockSize - 1;// 计算每个线程下载的结束位置
                    // 如果是最后一个线程，结束位置需要单独计算
                    if (threadId == threadCount - 1) {
                        endIndex = filelength - 1;
                    }
                    // 4.开启线程去执行下载
                    new Thread(new DownloadThread(threadId, startIndex, endIndex)).start();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    public static class DownloadThread implements Runnable{
        private int threadId;
        private int startIndex;
        private int endIndex;
        private int lastPostion;

        public DownloadThread(int threadId, int startIndex, int endIndex) {
            this.threadId = threadId;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }

        @Override
        public void run() {
            synchronized (DownloadThread.class) {//静态类的锁是当前类所属的字节码文件对象
                runningTrheadCount = runningTrheadCount + 1;// 开启一线程，线程数加1
            }
            // 分段请求网络连接，分段保存文件到本地
            try {
                URL url = new URL(path);
                HttpURLConnection openConnection = (HttpURLConnection) url.openConnection();
                openConnection.setRequestMethod("GET");
                openConnection.setConnectTimeout(10 * 1000);
                System.out.println("理论上下载：  线程：" + threadId + "，开始位置：" + startIndex + ";结束位置:" + endIndex);
                // 读取上次下载结束的位置,本次从这个位置开始直接下载。
                File file2 = new File("/Users/zhaoxiaotong/Downloads/"+threadId + ".png");
                if (file2.exists()) {
                    BufferedReader bufferedReader = new BufferedReader(
                            new InputStreamReader(new FileInputStream(clientPath+file2)));

                    String lastPostion_str = bufferedReader.readLine();
                    lastPostion = Integer.parseInt(lastPostion_str);// 读取文件获取上次下载的位置
                    bufferedReader.close();
                } else {
                    lastPostion = startIndex;
                }
                // 设置分段下载的头信息。 Range:做分段数据请求用的。
                // bytes:0-500:请求服务器资源中0-500之间的字节信息
                openConnection.setRequestProperty("Range", "bytes:" + lastPostion + "-" + endIndex);
                System.out.println("实际下载：  线程：" + threadId + "，开始位置：" + lastPostion + ";结束位置:" + endIndex);

                if (openConnection.getResponseCode() == 200) {// 200：请求全部资源成功，
                    // 206代表部分资源请求成功
                    InputStream inputStream = openConnection.getInputStream();
                    // 请求成功将流写入本地文件中，已经创建的占位那个文件中
                    RandomAccessFile randomAccessFile = new RandomAccessFile(new File(clientPath+getFileName(path)), "rw");
                    randomAccessFile.seek(lastPostion);// 设置随机文件从哪个位置开始写。
                    // 将流中的数据写入文件
                    byte[] buffer = new byte[1024];//定义一个1k的缓冲字节数组
                    int length = -1;
                    int total = 0;// 记录本次线程下载的总大小
                    while ((length = inputStream.read(buffer)) != -1) {
                        //开始写入到本地文件
                        randomAccessFile.write(buffer, 0, length);
                        total += length;
                        // 去保存当前线程下载的位置，保存到文件中
                        int currentThreadPostion = lastPostion + total;// 计算出当前线程本次下载的位置
                        // 创建临时的    随机文件保存当前线程下载的位置
                        File file = new File(clientPath+threadId + ".png");
                        RandomAccessFile accessfile = new RandomAccessFile(file, "rwd");
                        accessfile.write(String.valueOf(currentThreadPostion).getBytes());
                        accessfile.close();
                    }
                    // 关闭相关的流信息
                    inputStream.close();
                    randomAccessFile.close();
                    System.out.println("线程：" + threadId + "，下载完毕");
                    // 当所有线程下载结束，删除存放下载位置的文件。
                    synchronized (DownloadThread.class) {
                        runningTrheadCount--;// 标志着一个线程下载结束。
                        if (runningTrheadCount == 0) {
                            System.out.println("所有线程下载完成");
                            for (int threadId = 0; threadId < threadCount; threadId++) {
                                File file = new File(clientPath+threadId + ".png");
                                System.out.println(file.getAbsolutePath());
                                file.delete();
                            }
                            System.out.println("合并完成!");
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static String getFileName(String url) {
        return url.substring(url.lastIndexOf("/") + 1);
    }
}
